package io;

import edu.uci.ics.jung.graph.Graph;
import facility.Facility;
import graph.CustNode;
import graph.NetworkPoint;
import graph.GraphEdge;
import graph.GraphNode;
import graph.NetEdge;
import graph.NetEdgeUB;
import graph.NetNode;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.junit.runner.Computer;

import route.Route;
import util.Sequencer;

public class DataReader {
	public static final String sep = ",";
	static double ratioForComputingScore = 0.001;

	/**
	 * load customer sites
	 * 
	 * format: cid,x,y
	 * 
	 * @param cNodesFile
	 * @return id->custnode
	 */
	public static HashMap<Integer, CustNode> loadCNodes(String cNodesFile,
			int numcNOdes) {
		HashMap<Integer, CustNode> nodeMap = null;
		if (numcNOdes <= 0) {
			nodeMap = new HashMap<Integer, CustNode>();
		} else {
			nodeMap = new HashMap<Integer, CustNode>(numcNOdes);
		}
		BufferedReader bufferedReader = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(cNodesFile));
			String line = null;
			StringTokenizer st = null;
			while ((line = bufferedReader.readLine()) != null) {
				if (line.startsWith("#")) {
					continue;
				}
				st = new StringTokenizer(line, sep);
				int id = Integer.parseInt(st.nextToken());
				double x = Double.parseDouble(st.nextToken());
				double y = Double.parseDouble(st.nextToken());
				nodeMap.put(id, new CustNode(id, x, y));

				// if (id > vertexCounter) {
				// vertexCounter = id;
				// }
			}

			bufferedReader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return nodeMap;

	}

	/**
	 * lines starting with # are comments
	 * format: vid,x,y
	 * 
	 * @param vertexFile
	 * @return id->graphnode
	 */
	public static HashMap<Integer, GraphNode> loadVertices(String vertexFile,
			int initSize) {
		HashMap<Integer, GraphNode> nodeMap = null;
		if (initSize <= 0) {
			nodeMap = new HashMap<Integer, GraphNode>();
		} else {
			nodeMap = new HashMap<Integer, GraphNode>(initSize);
		}
		BufferedReader bufferedReader = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(vertexFile));
			String line = null;
			StringTokenizer st = null;
			while ((line = bufferedReader.readLine()) != null) {
				if (line.startsWith("#")) {
					continue;
				}
				st = new StringTokenizer(line, sep);
				int id = Integer.parseInt(st.nextToken());
				double x = Double.parseDouble(st.nextToken());
				double y = Double.parseDouble(st.nextToken());
				nodeMap.put(id, new NetNode(id, x, y));

				// if (id > vertexCounter) {
				// vertexCounter = id;
				// }
			}

			bufferedReader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return nodeMap;

	}

	/**
	 * lines starting with # are comments
	 * fomat: eid,snodeid,enodeid
	 * 
	 * @param edgeFile
	 * @return the list of edges from the file
	 */
	public static HashMap<Integer, GraphEdge> loadEdges(
			Map<Integer, GraphNode> verticesMap, String edgeFile, int method,
			int initSize) {
		HashMap<Integer, GraphEdge> edges = null;
		if (initSize <= 0) {
			edges = new HashMap<Integer, GraphEdge>();
		} else {
			edges = new HashMap<Integer, GraphEdge>(initSize);
		}
		BufferedReader bufferedReader = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(edgeFile));
			String line = null;
			StringTokenizer st = null;
			while ((line = bufferedReader.readLine()) != null) {
				if (line.startsWith("#")) {
					continue;
				}
				st = new StringTokenizer(line, DataReader.sep);
				// Process the data
				int id = Integer.parseInt(st.nextToken());
				int fromId = Integer.parseInt(st.nextToken());
				int toId = Integer.parseInt(st.nextToken());

				GraphNode sourceGn = verticesMap.get(fromId);
				GraphNode destGn = verticesMap.get(toId);

				// double[] sTemp = { sourceGn.x, sourceGn.y };
				// double[] dTemp = { destGn.x, destGn.y };
				if (method == 1) {
					// graph augmentation use netEdge
					edges.put(id, new NetEdge(id, sourceGn, destGn));
				} else if (method == 2) {
					// iterative partitioning
					edges.put(id, new NetEdgeUB(id, sourceGn, destGn));
				}
				// need to give it node

				// if (edgeCounter < id) {
				// edgeCounter++;
				// }
			}

			bufferedReader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return edges;
	}

	/**
	 * format: fid,mpx,mpy,eid,ratioFromNode
	 * 
	 * loadFacility method for findOptSegAug f knows edgeid, edge knows fid
	 * 
	 * @param edgeFile
	 * @param edgesMap
	 * @return a list of facilities already embedded into graph
	 */
	public static Map<Integer, Facility> loadFacilities(String facilitiesFile,
			HashMap<Integer, GraphEdge> edgesMap, int numFacilitiesToRead) {
		Map<Integer, Facility> fs = new HashMap<Integer, Facility>(
				numFacilitiesToRead);
		BufferedReader bufferedReader = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(facilitiesFile));
			String line = null;
			StringTokenizer st = null;
			while ((line = bufferedReader.readLine()) != null
					&& fs.size() <= numFacilitiesToRead) {
				if (line.startsWith("#")) {
					continue;
				}
				st = new StringTokenizer(line, DataReader.sep);
				// Process the data
				int id = Integer.parseInt(st.nextToken());
				double x = Double.parseDouble(st.nextToken());
				double y = Double.parseDouble(st.nextToken());
				int edgeId = Integer.parseInt(st.nextToken());
				double ratioFromNode = Double.parseDouble(st.nextToken());
				// double distFromNode = Double.parseDouble(st.nextToken());
				//
				// Facility f = new Facility(id, x, y, edgeId, ratioFromNode,
				// distFromNode);
				Facility f = new Facility(id, x, y, edgeId, ratioFromNode);
				fs.put(id, f);
				edgesMap.get(edgeId).addFacilityId(id);
			}

			bufferedReader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return fs;
	}

	/**
	 * format: routeId numRouteNodes numUsages
	 * sequence of pairs of (x,y) for each node
	 * list of usages
	 * 
	 * @param routesFile
	 * @return map from id to route
	 */
	public static HashMap<Integer, Route> loadRoutes(String routesFile,
			Map<Integer, GraphEdge> edgesMap, int numRoutesToRead) {
		HashMap<Integer, Route> routeMap = new HashMap<Integer, Route>(
				numRoutesToRead);
		BufferedReader bufferedReader = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(routesFile));
			String line = null;
			StringTokenizer st = null;

			int routeId = 0;
			int numRouteNodes = 0;
			int numUsages = 0;
			int lineNum = 0;
			while ((line = bufferedReader.readLine()) != null
					&& routeMap.size() <= numRoutesToRead) {
				lineNum++;
				if (line.startsWith("#")) {
					continue;
				}
				st = new StringTokenizer(line, DataReader.sep);

				// id, score, usage
				routeId = Integer.parseInt(st.nextToken());
				numRouteNodes = Integer.parseInt(st.nextToken());
				numUsages = Integer.parseInt(st.nextToken());

				NetworkPoint[] nodes = new NetworkPoint[numRouteNodes];

				int[] usages = new int[numUsages];
				int i = 0;
				// read x,y routeNode
				while (i < numRouteNodes
						&& (line = bufferedReader.readLine()) != null) {
					lineNum++;

					StringTokenizer st1 = new StringTokenizer(line,
							DataReader.sep);
					if (st1.countTokens() == 6) {
						double origx = Double.parseDouble(st1.nextToken());
						double origy = Double.parseDouble(st1.nextToken());
						int edgeId = Integer.parseInt(st1.nextToken());
						double matchedx = Double.parseDouble(st1.nextToken());
						double matchedy = Double.parseDouble(st1.nextToken());
						double ratioFromNode = Double.parseDouble(st1
								.nextToken());
						// double distFromNode = Double.parseDouble(st1
						// .nextToken());

						// nodes[i++] = new RouteNode(Sequencer.next(),
						// origx,
						// origy, edgeId, matchedx, matchedy,
						// ratioFromNode, distFromNode);

						nodes[i++] = new NetworkPoint(Sequencer.next(), edgeId,
								ratioFromNode, matchedx, matchedy);

						// route id is added into edges here
						edgesMap.get(edgeId).addRouteId(routeId);
					} else {
						System.err.println("numLine read: " + lineNum);
						System.err.println(line);
						System.exit(0);
						break;
					}
				}
				// read usagse
				i = 0;
				while (i < numUsages
						&& (line = bufferedReader.readLine()) != null) {
					lineNum++;
					usages[i++] = Integer.parseInt(line);
				}

				// NetworkPoint[] elocs = new NetworkPoint[nodes.length];
				// for (int j = 0; j < nodes.length; j++) {
				// elocs[j] = nodes[j].getEloc();
				// }

				double length = computeRouteLength(nodes, edgesMap);
				double score = computeRouteScore(length, usages,
						ratioForComputingScore);

				Route r = new Route(routeId, nodes, length, score);
				routeMap.put(routeId, r);

			}

			bufferedReader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return routeMap;
	}

	private static double computeRouteScore(double routeLength, int[] usages,
			double ratio) {
		int sum = 0;
		for (Integer i : usages) {
			sum += i;
		}
		return routeLength * sum * ratio;
	}

	static double computeRouteLength(NetworkPoint[] routePoints,
			Map<Integer, GraphEdge> edgesMap) {

		double length = 0;
		for (int i = 0; i < routePoints.length - 2; i++) {
			NetworkPoint currP = routePoints[i];
			GraphEdge currEdge = edgesMap.get(routePoints[i].getEdgeId());
			GraphNode currSrc = currEdge.getSrcNode();
			GraphNode currDest = currEdge.getDestNode();

			NetworkPoint nextP = routePoints[i + 1];
			GraphEdge nextEdge = edgesMap.get(routePoints[i + 1].getEdgeId());
			GraphNode nextSrc = nextEdge.getSrcNode();
			GraphNode nextDest = nextEdge.getDestNode();

			if (currDest.equals(nextSrc)) {
				length += currEdge.getWeight() * (1 - currP.getRatioFromNode())
						+ nextEdge.getWeight() * nextP.getRatioFromNode();
			} else if (currSrc.equals(nextSrc)) {
				length += currEdge.getWeight() * currP.getRatioFromNode()
						+ nextEdge.getWeight() * nextP.getRatioFromNode();
			} else if (currSrc.equals(nextDest)) {
				length += currEdge.getWeight() * currP.getRatioFromNode()
						+ nextEdge.getWeight() * (1 - nextP.getRatioFromNode());
			} else if (currDest.equals(nextDest)) {
				length += currEdge.getWeight() * (1 - currP.getRatioFromNode())
						+ nextEdge.getWeight() * (1 - nextP.getRatioFromNode());
			}

		}
		return length;
	}

}
